scrolledwindow: port to use a gadget
authorCosimo Cecchi <cosimoc@gnome.org>
Wed, 23 Dec 2015 17:54:42 +0000 (09:54 -0800)
committerCosimo Cecchi <cosimoc@gnome.org>
Tue, 29 Dec 2015 21:51:06 +0000 (13:51 -0800)
gtk/gtkscrolledwindow.c

index 950ad3d3adeef5f837f722346c8f2cf65b53ae85..9e1a943eb7b2899c51aa220ffd1cce2b7e3be027 100644 (file)
@@ -29,6 +29,7 @@
 #include "gtkadjustment.h"
 #include "gtkadjustmentprivate.h"
 #include "gtkbindings.h"
+#include "gtkcsscustomgadgetprivate.h"
 #include "gtkdnd.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
@@ -210,6 +211,7 @@ struct _GtkScrolledWindowPrivate
   GtkWidget     *hscrollbar;
   GtkWidget     *vscrollbar;
 
+  GtkCssGadget  *gadget;
   GtkCssNode    *overshoot_node[4];
   GtkCssNode    *undershoot_node[4];
 
@@ -329,6 +331,13 @@ static void     gtk_scrolled_window_move_focus_out     (GtkScrolledWindow *scrol
 
 static void     gtk_scrolled_window_relative_allocation(GtkWidget         *widget,
                                                         GtkAllocation     *allocation);
+static void     gtk_scrolled_window_inner_allocation   (GtkWidget         *widget,
+                                                        GtkAllocation     *rect);
+static void     gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
+                                                        GtkWidget         *scrollbar,
+                                                        GtkAllocation     *allocation);
+static void     gtk_scrolled_window_allocate_child     (GtkScrolledWindow *swindow,
+                                                        GtkAllocation     *relative_allocation);
 static void     gtk_scrolled_window_adjustment_changed (GtkAdjustment     *adjustment,
                                                         gpointer           data);
 static void     gtk_scrolled_window_adjustment_value_changed (GtkAdjustment     *adjustment,
@@ -1376,220 +1385,836 @@ captured_event_cb (GtkWidget *widget,
   return GDK_EVENT_PROPAGATE;
 }
 
-static void
-gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
-{
-  GtkWidget *widget = GTK_WIDGET (scrolled_window);
-  GtkScrolledWindowPrivate *priv;
-  GtkCssNode *widget_node;
-  GQuark classes[4] = {
-    g_quark_from_static_string (GTK_STYLE_CLASS_LEFT),
-    g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT),
-    g_quark_from_static_string (GTK_STYLE_CLASS_TOP),
-    g_quark_from_static_string (GTK_STYLE_CLASS_BOTTOM),
-  };
-  gint i;
-
-  scrolled_window->priv = priv =
-    gtk_scrolled_window_get_instance_private (scrolled_window);
-
-  gtk_widget_set_has_window (widget, TRUE);
-  gtk_widget_set_can_focus (widget, TRUE);
-
-  /* Instantiated by gtk_scrolled_window_set_[hv]adjustment
-   * which are both construct properties
-   */
-  priv->hscrollbar = NULL;
-  priv->vscrollbar = NULL;
-  priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
-  priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
-  priv->hscrollbar_visible = FALSE;
-  priv->vscrollbar_visible = FALSE;
-  priv->focus_out = FALSE;
-  priv->window_placement = GTK_CORNER_TOP_LEFT;
-  priv->min_content_width = -1;
-  priv->min_content_height = -1;
-
-  priv->overlay_scrolling = TRUE;
-
-  priv->drag_gesture = gtk_gesture_drag_new (widget);
-  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE);
-  g_signal_connect_swapped (priv->drag_gesture, "drag-begin",
-                            G_CALLBACK (scrolled_window_drag_begin_cb),
-                            scrolled_window);
-  g_signal_connect_swapped (priv->drag_gesture, "drag-update",
-                            G_CALLBACK (scrolled_window_drag_update_cb),
-                            scrolled_window);
-  g_signal_connect_swapped (priv->drag_gesture, "end",
-                            G_CALLBACK (scrolled_window_drag_end_cb),
-                            scrolled_window);
-
-  priv->pan_gesture = gtk_gesture_pan_new (widget, GTK_ORIENTATION_VERTICAL);
-  gtk_gesture_group (priv->pan_gesture, priv->drag_gesture);
-  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->pan_gesture), TRUE);
-
-  priv->swipe_gesture = gtk_gesture_swipe_new (widget);
-  gtk_gesture_group (priv->swipe_gesture, priv->drag_gesture);
-  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->swipe_gesture), TRUE);
-  g_signal_connect_swapped (priv->swipe_gesture, "swipe",
-                            G_CALLBACK (scrolled_window_swipe_cb),
-                            scrolled_window);
-  priv->long_press_gesture = gtk_gesture_long_press_new (widget);
-  gtk_gesture_group (priv->long_press_gesture, priv->drag_gesture);
-  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE);
-  g_signal_connect_swapped (priv->long_press_gesture, "pressed",
-                            G_CALLBACK (scrolled_window_long_press_cb),
-                            scrolled_window);
-  g_signal_connect_swapped (priv->long_press_gesture, "cancelled",
-                            G_CALLBACK (scrolled_window_long_press_cancelled_cb),
-                            scrolled_window);
-
-  priv->scroll_history = g_array_new (FALSE, FALSE, sizeof (ScrollHistoryElem));
-
-  gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
-  gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
-
-  _gtk_widget_set_captured_event_handler (widget, captured_event_cb);
-
-  widget_node = gtk_widget_get_css_node (widget);
-  for (i = 0; i < 4; i++)
-    {
-      priv->overshoot_node[i] = gtk_css_node_new ();
-      gtk_css_node_set_name (priv->overshoot_node[i], I_("overshoot"));
-      gtk_css_node_add_class (priv->overshoot_node[i], classes[i]);
-      gtk_css_node_set_parent (priv->overshoot_node[i], widget_node);
-      gtk_css_node_set_state (priv->overshoot_node[i], gtk_css_node_get_state (widget_node));
-      g_object_unref (priv->overshoot_node[i]);
-
-      priv->undershoot_node[i] = gtk_css_node_new ();
-      gtk_css_node_set_name (priv->undershoot_node[i], I_("undershoot"));
-      gtk_css_node_add_class (priv->undershoot_node[i], classes[i]);
-      gtk_css_node_set_parent (priv->undershoot_node[i], widget_node);
-      gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node));
-      g_object_unref (priv->undershoot_node[i]);
-    }
-}
-
-/**
- * gtk_scrolled_window_new:
- * @hadjustment: (allow-none): horizontal adjustment
- * @vadjustment: (allow-none): vertical adjustment
- *
- * Creates a new scrolled window.
+/*
+ * _gtk_scrolled_window_get_spacing:
+ * @scrolled_window: a scrolled window
  *
- * The two arguments are the scrolled window’s adjustments; these will be
- * shared with the scrollbars and the child widget to keep the bars in sync 
- * with the child. Usually you want to pass %NULL for the adjustments, which 
- * will cause the scrolled window to create them for you.
+ * Gets the spacing between the scrolled window’s scrollbars and
+ * the scrolled widget. Used by GtkCombo
  *
- * Returns: a new scrolled window
+ * Returns: the spacing, in pixels.
  */
-GtkWidget*
-gtk_scrolled_window_new (GtkAdjustment *hadjustment,
-                        GtkAdjustment *vadjustment)
+static gint
+_gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
 {
-  GtkWidget *scrolled_window;
+  GtkScrolledWindowClass *class;
 
-  if (hadjustment)
-    g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
+  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
 
-  if (vadjustment)
-    g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
+  class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
 
-  scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
-                                   "hadjustment", hadjustment,
-                                   "vadjustment", vadjustment,
-                                   NULL);
+  if (class->scrollbar_spacing >= 0)
+    return class->scrollbar_spacing;
+  else
+    {
+      gint scrollbar_spacing;
 
-  return scrolled_window;
+      gtk_widget_style_get (GTK_WIDGET (scrolled_window),
+                           "scrollbar-spacing", &scrollbar_spacing,
+                           NULL);
+
+      return scrollbar_spacing;
+    }
 }
 
-/**
- * gtk_scrolled_window_set_hadjustment:
- * @scrolled_window: a #GtkScrolledWindow
- * @hadjustment: horizontal scroll adjustment
- *
- * Sets the #GtkAdjustment for the horizontal scrollbar.
- */
-void
-gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
-                                    GtkAdjustment     *hadjustment)
+static void
+gtk_scrolled_window_allocate (GtkCssGadget        *gadget,
+                              const GtkAllocation *allocation,
+                              int                  baseline,
+                              GtkAllocation       *out_clip,
+                              gpointer             data)
 {
-  GtkScrolledWindowPrivate *priv;
+  GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
+  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
   GtkBin *bin;
+  GtkAllocation relative_allocation;
+  GtkAllocation child_allocation;
   GtkWidget *child;
-
-  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
-  if (hadjustment)
-    g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
-  else
-    hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
+  gint sb_spacing;
+  gint sb_width;
+  gint sb_height;
 
   bin = GTK_BIN (scrolled_window);
-  priv = scrolled_window->priv;
-
-  if (!priv->hscrollbar)
-    {
-      priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment);
 
-      gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window));
-      g_object_ref (priv->hscrollbar);
-      gtk_widget_show (priv->hscrollbar);
-      update_scrollbar_positions (scrolled_window);
-    }
-  else
-    {
-      GtkAdjustment *old_adjustment;
+  /* Get possible scrollbar dimensions */
+  sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+  gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
+  gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
 
-      old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
-      if (old_adjustment == hadjustment)
-       return;
+  if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
+    priv->hscrollbar_visible = TRUE;
+  else if (priv->hscrollbar_policy == GTK_POLICY_NEVER ||
+           priv->hscrollbar_policy == GTK_POLICY_EXTERNAL)
+    priv->hscrollbar_visible = FALSE;
 
-      g_signal_handlers_disconnect_by_func (old_adjustment,
-                                           gtk_scrolled_window_adjustment_changed,
-                                           scrolled_window);
-      gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
-      gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar), hadjustment);
-    }
-  hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
-  g_signal_connect (hadjustment,
-                    "changed",
-                   G_CALLBACK (gtk_scrolled_window_adjustment_changed),
-                   scrolled_window);
-  g_signal_connect (hadjustment,
-                    "value-changed",
-                   G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
-                   scrolled_window);
-  gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
-  gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
+  if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
+    priv->vscrollbar_visible = TRUE;
+  else if (priv->vscrollbar_policy == GTK_POLICY_NEVER ||
+           priv->vscrollbar_policy == GTK_POLICY_EXTERNAL)
+    priv->vscrollbar_visible = FALSE;
 
   child = gtk_bin_get_child (bin);
-  if (GTK_IS_SCROLLABLE (child))
-    gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (child), hadjustment);
+  if (child && gtk_widget_get_visible (child))
+    {
+      gint child_scroll_width;
+      gint child_scroll_height;
+      GtkScrollablePolicy hscroll_policy;
+      GtkScrollablePolicy vscroll_policy;
+      gboolean previous_hvis;
+      gboolean previous_vvis;
+      guint count = 0;
 
-  if (gtk_scrolled_window_should_animate (scrolled_window))
-    gtk_adjustment_enable_animation (hadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);
-  g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HADJUSTMENT]);
-}
+      hscroll_policy = GTK_IS_SCROLLABLE (child)
+                       ? gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child))
+                       : GTK_SCROLL_MINIMUM;
+      vscroll_policy = GTK_IS_SCROLLABLE (child)
+                       ? gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child))
+                       : GTK_SCROLL_MINIMUM;
 
-/**
- * gtk_scrolled_window_set_vadjustment:
- * @scrolled_window: a #GtkScrolledWindow
- * @vadjustment: vertical scroll adjustment
- *
- * Sets the #GtkAdjustment for the vertical scrollbar.
- */
-void
-gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
-                                     GtkAdjustment     *vadjustment)
-{
-  GtkScrolledWindowPrivate *priv;
-  GtkBin *bin;
-  GtkWidget *child;
+      /* Determine scrollbar visibility first via hfw apis */
+      if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+       {
+         if (hscroll_policy == GTK_SCROLL_MINIMUM)
+           gtk_widget_get_preferred_width (child, &child_scroll_width, NULL);
+         else
+           gtk_widget_get_preferred_width (child, NULL, &child_scroll_width);
 
-  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
-  if (vadjustment)
-    g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
+         if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
+           {
+             /* First try without a vertical scrollbar if the content will fit the height
+              * given the extra width of the scrollbar */
+             if (vscroll_policy == GTK_SCROLL_MINIMUM)
+               gtk_widget_get_preferred_height_for_width (child,
+                                                          MAX (allocation->width, child_scroll_width),
+                                                          &child_scroll_height, NULL);
+             else
+               gtk_widget_get_preferred_height_for_width (child,
+                                                          MAX (allocation->width, child_scroll_width),
+                                                          NULL, &child_scroll_height);
+
+             if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
+               {
+                 /* Does the content height fit the allocation height ? */
+                 priv->vscrollbar_visible = child_scroll_height > allocation->height;
+
+                 /* Does the content width fit the allocation with minus a possible scrollbar ? */
+                 priv->hscrollbar_visible =
+                   child_scroll_width > allocation->width -
+                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
+
+                 /* Now that we've guessed the hscrollbar, does the content height fit
+                  * the possible new allocation height ?
+                  */
+                 priv->vscrollbar_visible =
+                   child_scroll_height > allocation->height -
+                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
+
+                 /* Now that we've guessed the vscrollbar, does the content width fit
+                  * the possible new allocation width ?
+                  */
+                 priv->hscrollbar_visible =
+                   child_scroll_width > allocation->width -
+                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
+               }
+             else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
+               {
+                 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
+                 priv->vscrollbar_visible = child_scroll_height > allocation->height -
+                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
+               }
+           }
+         else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
+           {
+             priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
+
+             if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
+               priv->hscrollbar_visible =
+                 child_scroll_width > allocation->width -
+                 (priv->vscrollbar_visible && !priv->use_indicators ? 0 : sb_width + sb_spacing);
+             else
+               priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
+           }
+       }
+      else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
+       {
+         if (vscroll_policy == GTK_SCROLL_MINIMUM)
+           gtk_widget_get_preferred_height (child, &child_scroll_height, NULL);
+         else
+           gtk_widget_get_preferred_height (child, NULL, &child_scroll_height);
+
+         if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
+           {
+             /* First try without a horizontal scrollbar if the content will fit the width
+              * given the extra height of the scrollbar */
+             if (hscroll_policy == GTK_SCROLL_MINIMUM)
+               gtk_widget_get_preferred_width_for_height (child,
+                                                          MAX (allocation->height, child_scroll_height),
+                                                          &child_scroll_width, NULL);
+             else
+               gtk_widget_get_preferred_width_for_height (child,
+                                                          MAX (allocation->height, child_scroll_height),
+                                                          NULL, &child_scroll_width);
+
+             if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
+               {
+                 /* Does the content width fit the allocation width ? */
+                 priv->hscrollbar_visible = child_scroll_width > allocation->width;
+
+                 /* Does the content height fit the allocation with minus a possible scrollbar ? */
+                 priv->vscrollbar_visible =
+                   child_scroll_height > allocation->height -
+                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
+
+                 /* Now that we've guessed the vscrollbar, does the content width fit
+                  * the possible new allocation width ?
+                  */
+                 priv->hscrollbar_visible =
+                   child_scroll_width > allocation->width -
+                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
+
+                 /* Now that we've guessed the hscrollbar, does the content height fit
+                  * the possible new allocation height ?
+                  */
+                 priv->vscrollbar_visible =
+                   child_scroll_height > allocation->height -
+                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
+               }
+             else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
+               {
+                 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
+                 priv->hscrollbar_visible = child_scroll_width > allocation->width -
+                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
+               }
+           }
+         else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
+           {
+             priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
+
+             if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
+               priv->vscrollbar_visible =
+                 child_scroll_height > allocation->height -
+                 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
+             else
+               priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
+           }
+       }
+
+      /* Now after guessing scrollbar visibility; fall back on the allocation loop which
+       * observes the adjustments to detect scrollbar visibility and also avoids
+       * infinite recursion
+       */
+      do
+       {
+         previous_hvis = priv->hscrollbar_visible;
+         previous_vvis = priv->vscrollbar_visible;
+         gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
+
+         /* Explicitly force scrollbar visibility checks.
+          *
+          * Since we make a guess above, the child might not decide to update the adjustments
+          * if they logically did not change since the last configuration
+          */
+         if (priv->hscrollbar)
+           gtk_scrolled_window_adjustment_changed
+              (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window);
+
+         if (priv->vscrollbar)
+           gtk_scrolled_window_adjustment_changed
+              (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window);
+
+         /* If, after the first iteration, the hscrollbar and the
+          * vscrollbar flip visiblity... or if one of the scrollbars flip
+          * on each itteration indefinitly/infinitely, then we just need both
+          * at this size.
+          */
+         if ((count &&
+              previous_hvis != priv->hscrollbar_visible &&
+              previous_vvis != priv->vscrollbar_visible) || count > 3)
+           {
+             priv->hscrollbar_visible = TRUE;
+             priv->vscrollbar_visible = TRUE;
+
+             gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
+
+             break;
+           }
+
+         count++;
+       }
+      while (previous_hvis != priv->hscrollbar_visible ||
+            previous_vvis != priv->vscrollbar_visible);
+    }
+  else
+    {
+      priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
+      priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
+      gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
+    }
+
+  gtk_widget_set_child_visible (priv->hscrollbar, priv->hscrollbar_visible);
+  if (priv->hscrollbar_visible)
+    {
+      gtk_scrolled_window_allocate_scrollbar (scrolled_window,
+                                              priv->hscrollbar,
+                                              &child_allocation);
+      if (priv->use_indicators)
+        {
+          gdk_window_move_resize (priv->hindicator.window,
+                                  child_allocation.x,
+                                  child_allocation.y,
+                                  child_allocation.width,
+                                  child_allocation.height);
+          child_allocation.x = 0;
+          child_allocation.y = 0;
+        }
+      gtk_widget_size_allocate (priv->hscrollbar, &child_allocation);
+    }
+
+  gtk_widget_set_child_visible (priv->vscrollbar, priv->vscrollbar_visible);
+  if (priv->vscrollbar_visible)
+    {
+      gtk_scrolled_window_allocate_scrollbar (scrolled_window,
+                                              priv->vscrollbar,
+                                              &child_allocation);
+      if (priv->use_indicators)
+        {
+          gdk_window_move_resize (priv->vindicator.window,
+                                  child_allocation.x,
+                                  child_allocation.y,
+                                  child_allocation.width,
+                                  child_allocation.height);
+          child_allocation.x = 0;
+          child_allocation.y = 0;
+        }
+      gtk_widget_size_allocate (priv->vscrollbar, &child_allocation);
+    }
+
+  gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);
+}
+
+static void
+gtk_scrolled_window_measure (GtkCssGadget   *gadget,
+                             GtkOrientation  orientation,
+                             int             for_size,
+                             int            *minimum_size,
+                             int            *natural_size,
+                             int            *minimum_baseline,
+                             int            *natural_baseline,
+                             gpointer        data)
+{
+  GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
+  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+  GtkBin *bin = GTK_BIN (scrolled_window);
+  gint extra_width;
+  gint extra_height;
+  gint scrollbar_spacing;
+  GtkRequisition hscrollbar_requisition;
+  GtkRequisition vscrollbar_requisition;
+  GtkRequisition minimum_req, natural_req;
+  GtkWidget *child;
+  gint min_child_size, nat_child_size;
+
+  scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+
+  extra_width = 0;
+  extra_height = 0;
+  minimum_req.width = 0;
+  minimum_req.height = 0;
+  natural_req.width = 0;
+  natural_req.height = 0;
+
+  gtk_widget_get_preferred_size (priv->hscrollbar,
+                                 &hscrollbar_requisition, NULL);
+  gtk_widget_get_preferred_size (priv->vscrollbar,
+                                 &vscrollbar_requisition, NULL);
+
+  child = gtk_bin_get_child (bin);
+  if (child && gtk_widget_get_visible (child))
+    {
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+       {
+         gtk_widget_get_preferred_width (child,
+                                          &min_child_size,
+                                          &nat_child_size);
+
+         if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
+           {
+             minimum_req.width += min_child_size;
+             natural_req.width += nat_child_size;
+           }
+         else
+           {
+             if (priv->min_content_width >= 0)
+               {
+                 minimum_req.width = MAX (minimum_req.width, priv->min_content_width);
+                 natural_req.width = MAX (natural_req.width, priv->min_content_width);
+                 extra_width = -1;
+               }
+             else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators)
+               {
+                 minimum_req.width += vscrollbar_requisition.width;
+                 natural_req.width += vscrollbar_requisition.width;
+               }
+           }
+       }
+      else /* GTK_ORIENTATION_VERTICAL */
+       {
+         gtk_widget_get_preferred_height (child,
+                                           &min_child_size,
+                                           &nat_child_size);
+
+         if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
+           {
+             minimum_req.height += min_child_size;
+             natural_req.height += nat_child_size;
+           }
+         else
+           {
+             if (priv->min_content_height >= 0)
+               {
+                 minimum_req.height = MAX (minimum_req.height, priv->min_content_height);
+                 natural_req.height = MAX (natural_req.height, priv->min_content_height);
+                 extra_height = -1;
+               }
+             else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators)
+               {
+                 minimum_req.height += vscrollbar_requisition.height;
+                 natural_req.height += vscrollbar_requisition.height;
+               }
+           }
+       }
+    }
+
+  if (policy_may_be_visible (priv->hscrollbar_policy) && !priv->use_indicators)
+    {
+      minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width);
+      natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width);
+      if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
+       extra_height = scrollbar_spacing + hscrollbar_requisition.height;
+    }
+
+  if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators)
+    {
+      minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height);
+      natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height);
+      if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
+       extra_width = scrollbar_spacing + vscrollbar_requisition.width;
+    }
+
+  minimum_req.width  += MAX (0, extra_width);
+  minimum_req.height += MAX (0, extra_height);
+  natural_req.width  += MAX (0, extra_width);
+  natural_req.height += MAX (0, extra_height);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      *minimum_size = minimum_req.width;
+      *natural_size = natural_req.width;
+    }
+  else
+    {
+      *minimum_size = minimum_req.height;
+      *natural_size = natural_req.height;
+    }
+}
+
+static void
+gtk_scrolled_window_draw_scrollbars_junction (GtkScrolledWindow *scrolled_window,
+                                              cairo_t *cr)
+{
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+  GtkWidget *widget = GTK_WIDGET (scrolled_window);
+  GtkAllocation content_allocation, hscr_allocation, vscr_allocation;
+  GtkStyleContext *context;
+  GdkRectangle junction_rect;
+  gboolean is_rtl;
+
+  is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+  gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation);
+  gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation);
+  gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation,
+                                         NULL);
+
+  junction_rect.x = content_allocation.x;
+  junction_rect.y = content_allocation.y;
+  junction_rect.width = vscr_allocation.width;
+  junction_rect.height = hscr_allocation.height;
+
+  if ((is_rtl &&
+       (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
+        priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
+      (!is_rtl &&
+       (priv->window_placement == GTK_CORNER_TOP_LEFT ||
+        priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
+    junction_rect.x += hscr_allocation.width;
+
+  if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
+      priv->window_placement == GTK_CORNER_TOP_RIGHT)
+    junction_rect.y += vscr_allocation.height;
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_save_named (context, "junction");
+
+  gtk_render_background (context, cr,
+                         junction_rect.x, junction_rect.y,
+                         junction_rect.width, junction_rect.height);
+  gtk_render_frame (context, cr,
+                    junction_rect.x, junction_rect.y,
+                    junction_rect.width, junction_rect.height);
+
+  gtk_style_context_restore (context);
+}
+
+static void
+gtk_scrolled_window_draw_overshoot (GtkScrolledWindow *scrolled_window,
+                                   cairo_t           *cr)
+{
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+  GtkWidget *widget = GTK_WIDGET (scrolled_window);
+  gint overshoot_x, overshoot_y;
+  GtkStyleContext *context;
+  GdkRectangle rect;
+
+  if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
+    return;
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_scrolled_window_inner_allocation (widget, &rect);
+
+  overshoot_x = CLAMP (overshoot_x, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
+  overshoot_y = CLAMP (overshoot_y, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
+
+  if (overshoot_x > 0)
+    {
+      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_RIGHT]);
+      gtk_render_background (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
+      gtk_render_frame (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
+      gtk_style_context_restore (context);
+    }
+  else if (overshoot_x < 0)
+    {
+      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_LEFT]);
+      gtk_render_background (context, cr, rect.x, rect.y, -overshoot_x, rect.height);
+      gtk_render_frame (context, cr, rect.x, rect.y, -overshoot_x, rect.height);
+      gtk_style_context_restore (context);
+    }
+
+  if (overshoot_y > 0)
+    {
+      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_BOTTOM]);
+      gtk_render_background (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
+      gtk_render_frame (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
+      gtk_style_context_restore (context);
+    }
+  else if (overshoot_y < 0)
+    {
+      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_TOP]);
+      gtk_render_background (context, cr, rect.x, rect.y, rect.width, -overshoot_y);
+      gtk_render_frame (context, cr, rect.x, rect.y, rect.width, -overshoot_y);
+      gtk_style_context_restore (context);
+    }
+}
+
+static void
+gtk_scrolled_window_draw_undershoot (GtkScrolledWindow *scrolled_window,
+                                     cairo_t           *cr)
+{
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+  GtkWidget *widget = GTK_WIDGET (scrolled_window);
+  GtkStyleContext *context;
+  GdkRectangle rect;
+  GtkAdjustment *adj;
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_scrolled_window_inner_allocation (widget, &rect);
+
+  adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
+  if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
+    {
+      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_RIGHT]);
+      gtk_render_background (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
+      gtk_render_frame (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
+
+      gtk_style_context_restore (context);
+    }
+  if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
+    {
+      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_LEFT]);
+      gtk_render_background (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
+      gtk_render_frame (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
+      gtk_style_context_restore (context);
+    }
+
+  adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
+  if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
+    {
+      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_BOTTOM]);
+      gtk_render_background (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
+      gtk_render_frame (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
+      gtk_style_context_restore (context);
+    }
+  if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
+    {
+      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_TOP]);
+      gtk_render_background (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
+      gtk_render_frame (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
+      gtk_style_context_restore (context);
+    }
+}
+
+static gboolean
+gtk_scrolled_window_render (GtkCssGadget *gadget,
+                            cairo_t      *cr,
+                            int           x,
+                            int           y,
+                            int           width,
+                            int           height,
+                            gpointer      data)
+{
+  GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
+  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+
+  if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
+    {
+      if (priv->hscrollbar_visible &&
+          priv->vscrollbar_visible)
+        gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr);
+    }
+
+  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr);
+
+  if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
+    {
+      gtk_scrolled_window_draw_undershoot (scrolled_window, cr);
+      gtk_scrolled_window_draw_overshoot (scrolled_window, cr);
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+{
+  GtkWidget *widget = GTK_WIDGET (scrolled_window);
+  GtkScrolledWindowPrivate *priv;
+  GtkCssNode *widget_node;
+  GQuark classes[4] = {
+    g_quark_from_static_string (GTK_STYLE_CLASS_LEFT),
+    g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT),
+    g_quark_from_static_string (GTK_STYLE_CLASS_TOP),
+    g_quark_from_static_string (GTK_STYLE_CLASS_BOTTOM),
+  };
+  gint i;
+
+  scrolled_window->priv = priv =
+    gtk_scrolled_window_get_instance_private (scrolled_window);
+
+  gtk_widget_set_has_window (widget, TRUE);
+  gtk_widget_set_can_focus (widget, TRUE);
+
+  /* Instantiated by gtk_scrolled_window_set_[hv]adjustment
+   * which are both construct properties
+   */
+  priv->hscrollbar = NULL;
+  priv->vscrollbar = NULL;
+  priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
+  priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
+  priv->hscrollbar_visible = FALSE;
+  priv->vscrollbar_visible = FALSE;
+  priv->focus_out = FALSE;
+  priv->window_placement = GTK_CORNER_TOP_LEFT;
+  priv->min_content_width = -1;
+  priv->min_content_height = -1;
+
+  priv->overlay_scrolling = TRUE;
+
+  priv->drag_gesture = gtk_gesture_drag_new (widget);
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE);
+  g_signal_connect_swapped (priv->drag_gesture, "drag-begin",
+                            G_CALLBACK (scrolled_window_drag_begin_cb),
+                            scrolled_window);
+  g_signal_connect_swapped (priv->drag_gesture, "drag-update",
+                            G_CALLBACK (scrolled_window_drag_update_cb),
+                            scrolled_window);
+  g_signal_connect_swapped (priv->drag_gesture, "end",
+                            G_CALLBACK (scrolled_window_drag_end_cb),
+                            scrolled_window);
+
+  priv->pan_gesture = gtk_gesture_pan_new (widget, GTK_ORIENTATION_VERTICAL);
+  gtk_gesture_group (priv->pan_gesture, priv->drag_gesture);
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->pan_gesture), TRUE);
+
+  priv->swipe_gesture = gtk_gesture_swipe_new (widget);
+  gtk_gesture_group (priv->swipe_gesture, priv->drag_gesture);
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->swipe_gesture), TRUE);
+  g_signal_connect_swapped (priv->swipe_gesture, "swipe",
+                            G_CALLBACK (scrolled_window_swipe_cb),
+                            scrolled_window);
+  priv->long_press_gesture = gtk_gesture_long_press_new (widget);
+  gtk_gesture_group (priv->long_press_gesture, priv->drag_gesture);
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE);
+  g_signal_connect_swapped (priv->long_press_gesture, "pressed",
+                            G_CALLBACK (scrolled_window_long_press_cb),
+                            scrolled_window);
+  g_signal_connect_swapped (priv->long_press_gesture, "cancelled",
+                            G_CALLBACK (scrolled_window_long_press_cancelled_cb),
+                            scrolled_window);
+
+  priv->scroll_history = g_array_new (FALSE, FALSE, sizeof (ScrollHistoryElem));
+
+  gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
+  gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
+
+  _gtk_widget_set_captured_event_handler (widget, captured_event_cb);
+
+  widget_node = gtk_widget_get_css_node (widget);
+  priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
+                                                     widget,
+                                                     gtk_scrolled_window_measure,
+                                                     gtk_scrolled_window_allocate,
+                                                     gtk_scrolled_window_render,
+                                                     NULL, NULL);
+  for (i = 0; i < 4; i++)
+    {
+      priv->overshoot_node[i] = gtk_css_node_new ();
+      gtk_css_node_set_name (priv->overshoot_node[i], I_("overshoot"));
+      gtk_css_node_add_class (priv->overshoot_node[i], classes[i]);
+      gtk_css_node_set_parent (priv->overshoot_node[i], widget_node);
+      gtk_css_node_set_state (priv->overshoot_node[i], gtk_css_node_get_state (widget_node));
+      g_object_unref (priv->overshoot_node[i]);
+
+      priv->undershoot_node[i] = gtk_css_node_new ();
+      gtk_css_node_set_name (priv->undershoot_node[i], I_("undershoot"));
+      gtk_css_node_add_class (priv->undershoot_node[i], classes[i]);
+      gtk_css_node_set_parent (priv->undershoot_node[i], widget_node);
+      gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node));
+      g_object_unref (priv->undershoot_node[i]);
+    }
+}
+
+/**
+ * gtk_scrolled_window_new:
+ * @hadjustment: (allow-none): horizontal adjustment
+ * @vadjustment: (allow-none): vertical adjustment
+ *
+ * Creates a new scrolled window.
+ *
+ * The two arguments are the scrolled window’s adjustments; these will be
+ * shared with the scrollbars and the child widget to keep the bars in sync
+ * with the child. Usually you want to pass %NULL for the adjustments, which
+ * will cause the scrolled window to create them for you.
+ *
+ * Returns: a new scrolled window
+ */
+GtkWidget*
+gtk_scrolled_window_new (GtkAdjustment *hadjustment,
+                        GtkAdjustment *vadjustment)
+{
+  GtkWidget *scrolled_window;
+
+  if (hadjustment)
+    g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
+
+  if (vadjustment)
+    g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
+
+  scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+                                   "hadjustment", hadjustment,
+                                   "vadjustment", vadjustment,
+                                   NULL);
+
+  return scrolled_window;
+}
+
+/**
+ * gtk_scrolled_window_set_hadjustment:
+ * @scrolled_window: a #GtkScrolledWindow
+ * @hadjustment: horizontal scroll adjustment
+ *
+ * Sets the #GtkAdjustment for the horizontal scrollbar.
+ */
+void
+gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+                                    GtkAdjustment     *hadjustment)
+{
+  GtkScrolledWindowPrivate *priv;
+  GtkBin *bin;
+  GtkWidget *child;
+
+  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
+  if (hadjustment)
+    g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
+  else
+    hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
+
+  bin = GTK_BIN (scrolled_window);
+  priv = scrolled_window->priv;
+
+  if (!priv->hscrollbar)
+    {
+      priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment);
+
+      gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window));
+      g_object_ref (priv->hscrollbar);
+      gtk_widget_show (priv->hscrollbar);
+      update_scrollbar_positions (scrolled_window);
+    }
+  else
+    {
+      GtkAdjustment *old_adjustment;
+
+      old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
+      if (old_adjustment == hadjustment)
+       return;
+
+      g_signal_handlers_disconnect_by_func (old_adjustment,
+                                           gtk_scrolled_window_adjustment_changed,
+                                           scrolled_window);
+      gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
+      gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar), hadjustment);
+    }
+  hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
+  g_signal_connect (hadjustment,
+                    "changed",
+                   G_CALLBACK (gtk_scrolled_window_adjustment_changed),
+                   scrolled_window);
+  g_signal_connect (hadjustment,
+                    "value-changed",
+                   G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
+                   scrolled_window);
+  gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
+  gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
+
+  child = gtk_bin_get_child (bin);
+  if (GTK_IS_SCROLLABLE (child))
+    gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (child), hadjustment);
+
+  if (gtk_scrolled_window_should_animate (scrolled_window))
+    gtk_adjustment_enable_animation (hadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);
+  g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HADJUSTMENT]);
+}
+
+/**
+ * gtk_scrolled_window_set_vadjustment:
+ * @scrolled_window: a #GtkScrolledWindow
+ * @vadjustment: vertical scroll adjustment
+ *
+ * Sets the #GtkAdjustment for the vertical scrollbar.
+ */
+void
+gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+                                     GtkAdjustment     *vadjustment)
+{
+  GtkScrolledWindowPrivate *priv;
+  GtkBin *bin;
+  GtkWidget *child;
+
+  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
+  if (vadjustment)
+    g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
   else
     vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
 
@@ -2077,6 +2702,7 @@ gtk_scrolled_window_destroy (GtkWidget *widget)
   g_clear_object (&priv->drag_gesture);
   g_clear_object (&priv->swipe_gesture);
   g_clear_object (&priv->long_press_gesture);
+  g_clear_object (&priv->gadget);
   g_clear_pointer (&priv->scroll_history, (GDestroyNotify) g_array_unref);
 
   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget);
@@ -2196,183 +2822,23 @@ gtk_scrolled_window_get_property (GObject    *object,
     }
 }
 
-static void
-gtk_scrolled_window_draw_scrollbars_junction (GtkScrolledWindow *scrolled_window,
-                                              cairo_t *cr)
-{
-  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
-  GtkWidget *widget = GTK_WIDGET (scrolled_window);
-  GtkAllocation hscr_allocation, vscr_allocation;
-  GtkStyleContext *context;
-  GdkRectangle junction_rect;
-  gboolean is_rtl;
-
-  is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
-  gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation);
-  gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation);
-
-  context = gtk_widget_get_style_context (widget);
-
-  if (priv->shadow_type != GTK_SHADOW_NONE)
-    {
-      GtkStateFlags state;
-      GtkBorder padding, border;
-
-      state = gtk_style_context_get_state (context);
-
-      gtk_style_context_get_padding (context, state, &padding);
-      gtk_style_context_get_border (context, state, &border);
-
-      junction_rect.x = padding.left + border.left;
-      junction_rect.y = padding.top + border.top;
-    }
-  else
-    {
-      junction_rect.x = 0;
-      junction_rect.y = 0;
-    }
-
-  junction_rect.width = vscr_allocation.width;
-  junction_rect.height = hscr_allocation.height;
-  
-  if ((is_rtl && 
-       (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
-        priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
-      (!is_rtl && 
-       (priv->window_placement == GTK_CORNER_TOP_LEFT ||
-        priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
-    junction_rect.x += hscr_allocation.width;
-
-  if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
-      priv->window_placement == GTK_CORNER_TOP_RIGHT)
-    junction_rect.y += vscr_allocation.height;
-
-  gtk_style_context_save_named (context, "junction");
-
-  gtk_render_background (context, cr,
-                         junction_rect.x, junction_rect.y,
-                         junction_rect.width, junction_rect.height);
-  gtk_render_frame (context, cr,
-                    junction_rect.x, junction_rect.y,
-                    junction_rect.width, junction_rect.height);
-
-  gtk_style_context_restore (context);
-}
-
-static void
-gtk_scrolled_window_inner_allocation (GtkWidget     *widget,
-                                      GtkAllocation *rect)
-{
-  GtkWidget *child;
-  GtkBorder border = { 0 };
-
-  gtk_scrolled_window_relative_allocation (widget, rect);
-
-  child = gtk_bin_get_child (GTK_BIN (widget));
-  if (GTK_IS_SCROLLABLE (child) &&
-      gtk_scrollable_get_border (GTK_SCROLLABLE (child), &border))
-    {
-      rect->x += border.left;
-      rect->y += border.top;
-      rect->width -= border.left + border.right;
-      rect->height -= border.top + border.bottom;
-    }
-}
-
-static void
-gtk_scrolled_window_draw_overshoot (GtkScrolledWindow *scrolled_window,
-                                   cairo_t           *cr)
-{
-  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
-  GtkWidget *widget = GTK_WIDGET (scrolled_window);
-  gint overshoot_x, overshoot_y;
-  GtkStyleContext *context;
-  GdkRectangle rect;
-
-  if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
-    return;
-
-  context = gtk_widget_get_style_context (widget);
-  gtk_scrolled_window_inner_allocation (widget, &rect);
-
-  overshoot_x = CLAMP (overshoot_x, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
-  overshoot_y = CLAMP (overshoot_y, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
-
-  if (overshoot_x > 0)
-    {
-      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_RIGHT]);
-      gtk_render_background (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
-      gtk_render_frame (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
-      gtk_style_context_restore (context);
-    }
-  else if (overshoot_x < 0)
-    {
-      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_LEFT]);
-      gtk_render_background (context, cr, rect.x, rect.y, -overshoot_x, rect.height);
-      gtk_render_frame (context, cr, rect.x, rect.y, -overshoot_x, rect.height);
-      gtk_style_context_restore (context);
-    }
-
-  if (overshoot_y > 0)
-    {
-      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_BOTTOM]);
-      gtk_render_background (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
-      gtk_render_frame (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
-      gtk_style_context_restore (context);
-    }
-  else if (overshoot_y < 0)
-    {
-      gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_TOP]);
-      gtk_render_background (context, cr, rect.x, rect.y, rect.width, -overshoot_y);
-      gtk_render_frame (context, cr, rect.x, rect.y, rect.width, -overshoot_y);
-      gtk_style_context_restore (context);
-    }
-}
-
-static void
-gtk_scrolled_window_draw_undershoot (GtkScrolledWindow *scrolled_window,
-                                     cairo_t           *cr)
-{
-  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
-  GtkWidget *widget = GTK_WIDGET (scrolled_window);
-  GtkStyleContext *context;
-  GdkRectangle rect;
-  GtkAdjustment *adj;
-
-  context = gtk_widget_get_style_context (widget);
-  gtk_scrolled_window_inner_allocation (widget, &rect);
-
-  adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
-  if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
-    {
-      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_RIGHT]);
-      gtk_render_background (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
-      gtk_render_frame (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
+static void
+gtk_scrolled_window_inner_allocation (GtkWidget     *widget,
+                                      GtkAllocation *rect)
+{
+  GtkWidget *child;
+  GtkBorder border = { 0 };
 
-      gtk_style_context_restore (context);
-    }
-  if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
-    {
-      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_LEFT]);
-      gtk_render_background (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
-      gtk_render_frame (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
-      gtk_style_context_restore (context);
-    }
+  gtk_scrolled_window_relative_allocation (widget, rect);
 
-  adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
-  if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
-    {
-      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_BOTTOM]);
-      gtk_render_background (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
-      gtk_render_frame (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
-      gtk_style_context_restore (context);
-    }
-  if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
+  child = gtk_bin_get_child (GTK_BIN (widget));
+  if (GTK_IS_SCROLLABLE (child) &&
+      gtk_scrollable_get_border (GTK_SCROLLABLE (child), &border))
     {
-      gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_TOP]);
-      gtk_render_background (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
-      gtk_render_frame (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
-      gtk_style_context_restore (context);
+      rect->x += border.left;
+      rect->y += border.top;
+      rect->width -= border.left + border.right;
+      rect->height -= border.top + border.bottom;
     }
 }
 
@@ -2382,33 +2848,8 @@ gtk_scrolled_window_draw (GtkWidget *widget,
 {
   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
-  GtkStyleContext *context;
-
-  if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
-    {
-      context = gtk_widget_get_style_context (widget);
-
-      gtk_render_background (context, cr,
-                             0, 0,
-                             gtk_widget_get_allocated_width (widget),
-                             gtk_widget_get_allocated_height (widget));
-      gtk_render_frame (context, cr,
-                        0, 0,
-                        gtk_widget_get_allocated_width (widget),
-                        gtk_widget_get_allocated_height (widget));
-
-      if (priv->hscrollbar_visible &&
-          priv->vscrollbar_visible)
-        gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr);
-    }
 
-  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr);
-
-  if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
-    {
-      gtk_scrolled_window_draw_undershoot (scrolled_window, cr);
-      gtk_scrolled_window_draw_overshoot (scrolled_window, cr);
-    }
+  gtk_css_gadget_draw (priv->gadget, cr);
 
   return FALSE;
 }
@@ -2571,7 +3012,7 @@ static void
 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
                                         GtkAllocation *allocation)
 {
-  GtkAllocation widget_allocation;
+  GtkAllocation content_allocation;
   GtkScrolledWindow *scrolled_window;
   GtkScrolledWindowPrivate *priv;
   gint sb_spacing;
@@ -2589,474 +3030,204 @@ gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
   gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
   gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
 
-  gtk_widget_get_allocation (widget, &widget_allocation);
-
-  allocation->x = 0;
-  allocation->y = 0;
-  allocation->width = widget_allocation.width;
-  allocation->height = widget_allocation.height;
-
-  /* Subtract some things from our available allocation size */
-  if (priv->shadow_type != GTK_SHADOW_NONE)
-    {
-      GtkStyleContext *context;
-      GtkStateFlags state;
-      GtkBorder padding, border;
-
-      context = gtk_widget_get_style_context (widget);
-      state = gtk_style_context_get_state (context);
-
-      gtk_style_context_get_border (context, state, &border);
-      gtk_style_context_get_padding (context, state, &padding);
-
-      allocation->x += padding.left + border.left;
-      allocation->y += padding.top + border.top;
-      allocation->width = MAX (1, allocation->width - (padding.left + border.left + padding.right + border.right));
-      allocation->height = MAX (1, allocation->height - (padding.top + border.top + padding.bottom + border.bottom));
-    }
-
-  if (priv->vscrollbar_visible && !priv->use_indicators)
-    {
-      gboolean is_rtl;
-
-      is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
-  
-      if ((!is_rtl && 
-          (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
-           priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
-         (is_rtl && 
-          (priv->window_placement == GTK_CORNER_TOP_LEFT ||
-           priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
-       allocation->x += (sb_width +  sb_spacing);
-
-      allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
-    }
-
-  if (priv->hscrollbar_visible && !priv->use_indicators)
-    {
-
-      if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT ||
-         priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)
-       allocation->y += (sb_height + sb_spacing);
-
-      allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
-    }
-}
-
-static gboolean
-_gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
-                                    gint              *overshoot_x,
-                                    gint              *overshoot_y)
-{
-  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
-  GtkAdjustment *vadjustment, *hadjustment;
-  gdouble lower, upper, x, y;
-
-  /* Vertical overshoot */
-  vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
-  lower = gtk_adjustment_get_lower (vadjustment);
-  upper = gtk_adjustment_get_upper (vadjustment) -
-    gtk_adjustment_get_page_size (vadjustment);
-
-  if (priv->unclamped_vadj_value < lower)
-    y = priv->unclamped_vadj_value - lower;
-  else if (priv->unclamped_vadj_value > upper)
-    y = priv->unclamped_vadj_value - upper;
-  else
-    y = 0;
-
-  /* Horizontal overshoot */
-  hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
-  lower = gtk_adjustment_get_lower (hadjustment);
-  upper = gtk_adjustment_get_upper (hadjustment) -
-    gtk_adjustment_get_page_size (hadjustment);
-
-  if (priv->unclamped_hadj_value < lower)
-    x = priv->unclamped_hadj_value - lower;
-  else if (priv->unclamped_hadj_value > upper)
-    x = priv->unclamped_hadj_value - upper;
-  else
-    x = 0;
-
-  if (overshoot_x)
-    *overshoot_x = x;
-
-  if (overshoot_y)
-    *overshoot_y = y;
-
-  return (x != 0 || y != 0);
-}
-
-static void
-gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
-                                   GtkAllocation     *relative_allocation)
-{
-  GtkWidget     *widget = GTK_WIDGET (swindow), *child;
-  GtkAllocation  child_allocation;
-
-  child = gtk_bin_get_child (GTK_BIN (widget));
-
-  gtk_scrolled_window_relative_allocation (widget, relative_allocation);
-
-  child_allocation.x = relative_allocation->x;
-  child_allocation.y = relative_allocation->y;
-  child_allocation.width = relative_allocation->width;
-  child_allocation.height = relative_allocation->height;
-
-  gtk_widget_size_allocate (child, &child_allocation);
-}
-
-static void
-gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
-                                        GtkWidget         *scrollbar,
-                                        GtkAllocation     *allocation)
-{
-  GtkAllocation child_allocation, content_allocation;
-  GtkWidget *widget = GTK_WIDGET (scrolled_window);
-  gint sb_spacing, sb_height, sb_width;
-  GtkScrolledWindowPrivate *priv;
-
-  priv = scrolled_window->priv;
-
-  gtk_scrolled_window_inner_allocation (widget, &content_allocation);
-  sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
-  gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
-  gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
-
-  if (scrollbar == priv->hscrollbar)
-    {
-      child_allocation.x = content_allocation.x;
-
-      if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
-         priv->window_placement == GTK_CORNER_TOP_RIGHT)
-        {
-          if (priv->use_indicators)
-           child_allocation.y = content_allocation.y + content_allocation.height - sb_height;
-          else
-           child_allocation.y = content_allocation.y + content_allocation.height + sb_spacing;
-        }
-      else
-        {
-          if (priv->use_indicators)
-           child_allocation.y = content_allocation.y;
-          else
-           child_allocation.y = content_allocation.y - sb_spacing - sb_height;
-        }
-
-      child_allocation.width = content_allocation.width;
-      child_allocation.height = sb_height;
-    }
-  else if (scrollbar == priv->vscrollbar)
-    {
-      if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
-          (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
-           priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
-         (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR &&
-          (priv->window_placement == GTK_CORNER_TOP_LEFT ||
-           priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
-        {
-          if (priv->use_indicators)
-           child_allocation.x = content_allocation.x + content_allocation.width - sb_width;
-          else
-           child_allocation.x = content_allocation.x + content_allocation.width + sb_spacing;
-        }
-      else
-        {
-          if (priv->use_indicators)
-           child_allocation.x = content_allocation.x;
-          else
-           child_allocation.x = content_allocation.x - sb_spacing - sb_width;
-        }
-
-      child_allocation.y = content_allocation.y;
-      child_allocation.width = sb_width;
-      child_allocation.height = content_allocation.height;
-    }
-
-  *allocation = child_allocation;
-}
-
-static void
-gtk_scrolled_window_size_allocate (GtkWidget     *widget,
-                                  GtkAllocation *allocation)
-{
-  GtkScrolledWindow *scrolled_window;
-  GtkScrolledWindowPrivate *priv;
-  GtkBin *bin;
-  GtkAllocation relative_allocation;
-  GtkAllocation child_allocation;
-  GtkWidget *child;
-  gint sb_spacing;
-  gint sb_width;
-  gint sb_height;
-
-  scrolled_window = GTK_SCROLLED_WINDOW (widget);
-  bin = GTK_BIN (scrolled_window);
-  priv = scrolled_window->priv;
-
-  if (gtk_widget_get_realized (widget))
-    {
-      gdk_window_move_resize (gtk_widget_get_window (widget),
-                              allocation->x, allocation->y,
-                              allocation->width, allocation->height);
-    }
-
-  /* Get possible scrollbar dimensions */
-  sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
-  gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
-  gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
-
-  gtk_widget_set_allocation (widget, allocation);
-
-  if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
-    priv->hscrollbar_visible = TRUE;
-  else if (priv->hscrollbar_policy == GTK_POLICY_NEVER ||
-           priv->hscrollbar_policy == GTK_POLICY_EXTERNAL)
-    priv->hscrollbar_visible = FALSE;
+  gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation,
+                                         NULL);
 
-  if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
-    priv->vscrollbar_visible = TRUE;
-  else if (priv->vscrollbar_policy == GTK_POLICY_NEVER ||
-           priv->vscrollbar_policy == GTK_POLICY_EXTERNAL)
-    priv->vscrollbar_visible = FALSE;
+  allocation->x = content_allocation.x;
+  allocation->y = content_allocation.y;
+  allocation->width = content_allocation.width;
+  allocation->height = content_allocation.height;
 
-  child = gtk_bin_get_child (bin);
-  if (child && gtk_widget_get_visible (child))
+  /* Subtract some things from our available allocation size */
+  if (priv->vscrollbar_visible && !priv->use_indicators)
     {
-      gint child_scroll_width;
-      gint child_scroll_height;
-      GtkScrollablePolicy hscroll_policy;
-      GtkScrollablePolicy vscroll_policy;
-      gboolean previous_hvis;
-      gboolean previous_vvis;
-      guint count = 0;
+      gboolean is_rtl;
 
-      hscroll_policy = GTK_IS_SCROLLABLE (child)
-                       ? gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child))
-                       : GTK_SCROLL_MINIMUM;
-      vscroll_policy = GTK_IS_SCROLLABLE (child)
-                       ? gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child))
-                       : GTK_SCROLL_MINIMUM;
+      is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
 
-      /* Determine scrollbar visibility first via hfw apis */
-      if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
-       {
-         if (hscroll_policy == GTK_SCROLL_MINIMUM)
-           gtk_widget_get_preferred_width (child, &child_scroll_width, NULL);
-         else
-           gtk_widget_get_preferred_width (child, NULL, &child_scroll_width);
+      if ((!is_rtl &&
+          (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
+           priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
+         (is_rtl &&
+          (priv->window_placement == GTK_CORNER_TOP_LEFT ||
+           priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
+       allocation->x += (sb_width +  sb_spacing);
 
-         if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
-           {
-             /* First try without a vertical scrollbar if the content will fit the height
-              * given the extra width of the scrollbar */
-             if (vscroll_policy == GTK_SCROLL_MINIMUM)
-               gtk_widget_get_preferred_height_for_width (child,
-                                                          MAX (allocation->width, child_scroll_width),
-                                                          &child_scroll_height, NULL);
-             else
-               gtk_widget_get_preferred_height_for_width (child,
-                                                          MAX (allocation->width, child_scroll_width),
-                                                          NULL, &child_scroll_height);
+      allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
+    }
 
-             if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
-               {
-                 /* Does the content height fit the allocation height ? */
-                 priv->vscrollbar_visible = child_scroll_height > allocation->height;
+  if (priv->hscrollbar_visible && !priv->use_indicators)
+    {
 
-                 /* Does the content width fit the allocation with minus a possible scrollbar ? */
-                 priv->hscrollbar_visible =
-                   child_scroll_width > allocation->width -
-                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
+      if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT ||
+         priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)
+       allocation->y += (sb_height + sb_spacing);
 
-                 /* Now that we've guessed the hscrollbar, does the content height fit
-                  * the possible new allocation height ?
-                  */
-                 priv->vscrollbar_visible =
-                   child_scroll_height > allocation->height -
-                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
+      allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
+    }
+}
 
-                 /* Now that we've guessed the vscrollbar, does the content width fit
-                  * the possible new allocation width ?
-                  */
-                 priv->hscrollbar_visible =
-                   child_scroll_width > allocation->width -
-                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
-               }
-             else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
-               {
-                 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
-                 priv->vscrollbar_visible = child_scroll_height > allocation->height -
-                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
-               }
-           }
-         else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
-           {
-             priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
+static gboolean
+_gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
+                                    gint              *overshoot_x,
+                                    gint              *overshoot_y)
+{
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+  GtkAdjustment *vadjustment, *hadjustment;
+  gdouble lower, upper, x, y;
 
-             if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
-               priv->hscrollbar_visible =
-                 child_scroll_width > allocation->width -
-                 (priv->vscrollbar_visible && !priv->use_indicators ? 0 : sb_width + sb_spacing);
-             else
-               priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
-           }
-       }
-      else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
-       {
-         if (vscroll_policy == GTK_SCROLL_MINIMUM)
-           gtk_widget_get_preferred_height (child, &child_scroll_height, NULL);
-         else
-           gtk_widget_get_preferred_height (child, NULL, &child_scroll_height);
+  /* Vertical overshoot */
+  vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
+  lower = gtk_adjustment_get_lower (vadjustment);
+  upper = gtk_adjustment_get_upper (vadjustment) -
+    gtk_adjustment_get_page_size (vadjustment);
 
-         if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
-           {
-             /* First try without a horizontal scrollbar if the content will fit the width
-              * given the extra height of the scrollbar */
-             if (hscroll_policy == GTK_SCROLL_MINIMUM)
-               gtk_widget_get_preferred_width_for_height (child,
-                                                          MAX (allocation->height, child_scroll_height),
-                                                          &child_scroll_width, NULL);
-             else
-               gtk_widget_get_preferred_width_for_height (child,
-                                                          MAX (allocation->height, child_scroll_height),
-                                                          NULL, &child_scroll_width);
+  if (priv->unclamped_vadj_value < lower)
+    y = priv->unclamped_vadj_value - lower;
+  else if (priv->unclamped_vadj_value > upper)
+    y = priv->unclamped_vadj_value - upper;
+  else
+    y = 0;
 
-             if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
-               {
-                 /* Does the content width fit the allocation width ? */
-                 priv->hscrollbar_visible = child_scroll_width > allocation->width;
+  /* Horizontal overshoot */
+  hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
+  lower = gtk_adjustment_get_lower (hadjustment);
+  upper = gtk_adjustment_get_upper (hadjustment) -
+    gtk_adjustment_get_page_size (hadjustment);
 
-                 /* Does the content height fit the allocation with minus a possible scrollbar ? */
-                 priv->vscrollbar_visible =
-                   child_scroll_height > allocation->height -
-                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
+  if (priv->unclamped_hadj_value < lower)
+    x = priv->unclamped_hadj_value - lower;
+  else if (priv->unclamped_hadj_value > upper)
+    x = priv->unclamped_hadj_value - upper;
+  else
+    x = 0;
 
-                 /* Now that we've guessed the vscrollbar, does the content width fit
-                  * the possible new allocation width ?
-                  */
-                 priv->hscrollbar_visible =
-                   child_scroll_width > allocation->width -
-                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
+  if (overshoot_x)
+    *overshoot_x = x;
 
-                 /* Now that we've guessed the hscrollbar, does the content height fit
-                  * the possible new allocation height ?
-                  */
-                 priv->vscrollbar_visible =
-                   child_scroll_height > allocation->height -
-                   (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
-               }
-             else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
-               {
-                 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
-                 priv->hscrollbar_visible = child_scroll_width > allocation->width -
-                   (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
-               }
-           }
-         else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
-           {
-             priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
+  if (overshoot_y)
+    *overshoot_y = y;
 
-             if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
-               priv->vscrollbar_visible =
-                 child_scroll_height > allocation->height -
-                 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
-             else
-               priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
-           }
-       }
+  return (x != 0 || y != 0);
+}
 
-      /* Now after guessing scrollbar visibility; fall back on the allocation loop which
-       * observes the adjustments to detect scrollbar visibility and also avoids
-       * infinite recursion
-       */
-      do
-       {
-         previous_hvis = priv->hscrollbar_visible;
-         previous_vvis = priv->vscrollbar_visible;
-         gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
+static void
+gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
+                                   GtkAllocation     *relative_allocation)
+{
+  GtkWidget     *widget = GTK_WIDGET (swindow), *child;
+  GtkAllocation  child_allocation;
 
-         /* Explicitly force scrollbar visibility checks.
-          *
-          * Since we make a guess above, the child might not decide to update the adjustments
-          * if they logically did not change since the last configuration
-          */
-         if (priv->hscrollbar)
-           gtk_scrolled_window_adjustment_changed
-              (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window);
+  child = gtk_bin_get_child (GTK_BIN (widget));
 
-         if (priv->vscrollbar)
-           gtk_scrolled_window_adjustment_changed
-              (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window);
+  gtk_scrolled_window_relative_allocation (widget, relative_allocation);
 
-         /* If, after the first iteration, the hscrollbar and the
-          * vscrollbar flip visiblity... or if one of the scrollbars flip
-          * on each itteration indefinitly/infinitely, then we just need both
-          * at this size.
-          */
-         if ((count &&
-              previous_hvis != priv->hscrollbar_visible &&
-              previous_vvis != priv->vscrollbar_visible) || count > 3)
-           {
-             priv->hscrollbar_visible = TRUE;
-             priv->vscrollbar_visible = TRUE;
+  child_allocation.x = relative_allocation->x;
+  child_allocation.y = relative_allocation->y;
+  child_allocation.width = relative_allocation->width;
+  child_allocation.height = relative_allocation->height;
 
-             gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
+  gtk_widget_size_allocate (child, &child_allocation);
+}
 
-             break;
-           }
+static void
+gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
+                                        GtkWidget         *scrollbar,
+                                        GtkAllocation     *allocation)
+{
+  GtkAllocation child_allocation, content_allocation;
+  GtkWidget *widget = GTK_WIDGET (scrolled_window);
+  gint sb_spacing, sb_height, sb_width;
+  GtkScrolledWindowPrivate *priv;
+
+  priv = scrolled_window->priv;
+
+  gtk_scrolled_window_inner_allocation (widget, &content_allocation);
+  sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+  gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
+  gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
 
-         count++;
-       }
-      while (previous_hvis != priv->hscrollbar_visible ||
-            previous_vvis != priv->vscrollbar_visible);
-    }
-  else
+  if (scrollbar == priv->hscrollbar)
     {
-      priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
-      priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
-      gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
-    }
+      child_allocation.x = content_allocation.x;
 
-  gtk_widget_set_child_visible (priv->hscrollbar, priv->hscrollbar_visible);
-  if (priv->hscrollbar_visible)
-    {
-      gtk_scrolled_window_allocate_scrollbar (scrolled_window,
-                                              priv->hscrollbar,
-                                              &child_allocation);
-      if (priv->use_indicators)
+      if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
+         priv->window_placement == GTK_CORNER_TOP_RIGHT)
         {
-          gdk_window_move_resize (priv->hindicator.window,
-                                  child_allocation.x,
-                                  child_allocation.y,
-                                  child_allocation.width,
-                                  child_allocation.height);
-          child_allocation.x = 0;
-          child_allocation.y = 0;
+          if (priv->use_indicators)
+           child_allocation.y = content_allocation.y + content_allocation.height - sb_height;
+          else
+           child_allocation.y = content_allocation.y + content_allocation.height + sb_spacing;
+        }
+      else
+        {
+          if (priv->use_indicators)
+           child_allocation.y = content_allocation.y;
+          else
+           child_allocation.y = content_allocation.y - sb_spacing - sb_height;
         }
-      gtk_widget_size_allocate (priv->hscrollbar, &child_allocation);
-    }
 
-  gtk_widget_set_child_visible (priv->vscrollbar, priv->vscrollbar_visible);
-  if (priv->vscrollbar_visible)
+      child_allocation.width = content_allocation.width;
+      child_allocation.height = sb_height;
+    }
+  else if (scrollbar == priv->vscrollbar)
     {
-      gtk_scrolled_window_allocate_scrollbar (scrolled_window,
-                                              priv->vscrollbar,
-                                              &child_allocation);
-      if (priv->use_indicators)
+      if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
+          (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
+           priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
+         (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR &&
+          (priv->window_placement == GTK_CORNER_TOP_LEFT ||
+           priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
         {
-          gdk_window_move_resize (priv->vindicator.window,
-                                  child_allocation.x,
-                                  child_allocation.y,
-                                  child_allocation.width,
-                                  child_allocation.height);
-          child_allocation.x = 0;
-          child_allocation.y = 0;
+          if (priv->use_indicators)
+           child_allocation.x = content_allocation.x + content_allocation.width - sb_width;
+          else
+           child_allocation.x = content_allocation.x + content_allocation.width + sb_spacing;
         }
-      gtk_widget_size_allocate (priv->vscrollbar, &child_allocation);
+      else
+        {
+          if (priv->use_indicators)
+           child_allocation.x = content_allocation.x;
+          else
+           child_allocation.x = content_allocation.x - sb_spacing - sb_width;
+        }
+
+      child_allocation.y = content_allocation.y;
+      child_allocation.width = sb_width;
+      child_allocation.height = content_allocation.height;
     }
 
-  gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);
+  *allocation = child_allocation;
+}
+
+static void
+gtk_scrolled_window_size_allocate (GtkWidget     *widget,
+                                  GtkAllocation *allocation)
+{
+  GtkScrolledWindow *scrolled_window;
+  GtkScrolledWindowPrivate *priv;
+  GtkAllocation clip, content_allocation;
+
+  scrolled_window = GTK_SCROLLED_WINDOW (widget);
+  priv = scrolled_window->priv;
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (gtk_widget_get_realized (widget))
+    gdk_window_move_resize (gtk_widget_get_window (widget),
+                            allocation->x, allocation->y,
+                            allocation->width, allocation->height);
+
+  content_allocation = *allocation;
+  content_allocation.x = content_allocation.y = 0;
+  gtk_css_gadget_allocate (priv->gadget,
+                           &content_allocation,
+                           gtk_widget_get_allocated_baseline (widget),
+                           &clip);
+
+  clip.x += allocation->x;
+  clip.y += allocation->y;
+  gtk_widget_set_clip (widget, &clip);
 }
 
 static gboolean
@@ -3657,184 +3828,16 @@ gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
   gtk_container_add (GTK_CONTAINER (viewport), child);
 }
 
-/*
- * _gtk_scrolled_window_get_spacing:
- * @scrolled_window: a scrolled window
- *
- * Gets the spacing between the scrolled window’s scrollbars and
- * the scrolled widget. Used by GtkCombo
- *
- * Returns: the spacing, in pixels.
- */
-static gint
-_gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
-{
-  GtkScrolledWindowClass *class;
-
-  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
-
-  class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
-
-  if (class->scrollbar_spacing >= 0)
-    return class->scrollbar_spacing;
-  else
-    {
-      gint scrollbar_spacing;
-
-      gtk_widget_style_get (GTK_WIDGET (scrolled_window),
-                           "scrollbar-spacing", &scrollbar_spacing,
-                           NULL);
-
-      return scrollbar_spacing;
-    }
-}
-
-static void
-gtk_scrolled_window_get_preferred_size (GtkWidget      *widget,
-                                        GtkOrientation  orientation,
-                                        gint           *minimum_size,
-                                        gint           *natural_size)
-{
-  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
-  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
-  GtkBin *bin = GTK_BIN (scrolled_window);
-  gint extra_width;
-  gint extra_height;
-  gint scrollbar_spacing;
-  GtkRequisition hscrollbar_requisition;
-  GtkRequisition vscrollbar_requisition;
-  GtkRequisition minimum_req, natural_req;
-  GtkWidget *child;
-  gint min_child_size, nat_child_size;
-
-  scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
-
-  extra_width = 0;
-  extra_height = 0;
-  minimum_req.width = 0;
-  minimum_req.height = 0;
-  natural_req.width = 0;
-  natural_req.height = 0;
-
-  gtk_widget_get_preferred_size (priv->hscrollbar,
-                                 &hscrollbar_requisition, NULL);
-  gtk_widget_get_preferred_size (priv->vscrollbar,
-                                 &vscrollbar_requisition, NULL);
-
-  child = gtk_bin_get_child (bin);
-  if (child && gtk_widget_get_visible (child))
-    {
-      if (orientation == GTK_ORIENTATION_HORIZONTAL)
-       {
-         gtk_widget_get_preferred_width (child,
-                                          &min_child_size,
-                                          &nat_child_size);
-
-         if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
-           {
-             minimum_req.width += min_child_size;
-             natural_req.width += nat_child_size;
-           }
-         else
-           {
-             if (priv->min_content_width >= 0)
-               {
-                 minimum_req.width = MAX (minimum_req.width, priv->min_content_width);
-                 natural_req.width = MAX (natural_req.width, priv->min_content_width);
-                 extra_width = -1;
-               }
-             else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators)
-               {
-                 minimum_req.width += vscrollbar_requisition.width;
-                 natural_req.width += vscrollbar_requisition.width;
-               }
-           }
-       }
-      else /* GTK_ORIENTATION_VERTICAL */
-       {
-         gtk_widget_get_preferred_height (child,
-                                           &min_child_size,
-                                           &nat_child_size);
-
-         if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
-           {
-             minimum_req.height += min_child_size;
-             natural_req.height += nat_child_size;
-           }
-         else
-           {
-             if (priv->min_content_height >= 0)
-               {
-                 minimum_req.height = MAX (minimum_req.height, priv->min_content_height);
-                 natural_req.height = MAX (natural_req.height, priv->min_content_height);
-                 extra_height = -1;
-               }
-             else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators)
-               {
-                 minimum_req.height += vscrollbar_requisition.height;
-                 natural_req.height += vscrollbar_requisition.height;
-               }
-           }
-       }
-    }
-
-  if (policy_may_be_visible (priv->hscrollbar_policy) && !priv->use_indicators)
-    {
-      minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width);
-      natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width);
-      if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
-       extra_height = scrollbar_spacing + hscrollbar_requisition.height;
-    }
-
-  if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators)
-    {
-      minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height);
-      natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height);
-      if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
-       extra_width = scrollbar_spacing + vscrollbar_requisition.width;
-    }
-
-  minimum_req.width  += MAX (0, extra_width);
-  minimum_req.height += MAX (0, extra_height);
-  natural_req.width  += MAX (0, extra_width);
-  natural_req.height += MAX (0, extra_height);
-
-  if (priv->shadow_type != GTK_SHADOW_NONE)
-    {
-      GtkStyleContext *context;
-      GtkStateFlags state;
-      GtkBorder padding, border;
-
-      context = gtk_widget_get_style_context (GTK_WIDGET (widget));
-      state = gtk_style_context_get_state (context);
-
-      gtk_style_context_get_padding (context, state, &padding);
-      gtk_style_context_get_border (context, state, &border);
-
-      minimum_req.width += padding.left + padding.right + border.left + border.right;
-      minimum_req.height += padding.top + padding.bottom + border.top + border.bottom;
-      natural_req.width += padding.left + padding.right + border.left + border.right;
-      natural_req.height += padding.top + padding.bottom + border.top + border.bottom;
-    }
-
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-      *minimum_size = minimum_req.width;
-      *natural_size = natural_req.width;
-    }
-  else
-    {
-      *minimum_size = minimum_req.height;
-      *natural_size = natural_req.height;
-    }
-}
-
 static void
 gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
                                          gint      *minimum_size,
                                          gint      *natural_size)
 {
-  gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+  gtk_css_gadget_get_preferred_size (GTK_SCROLLED_WINDOW (widget)->priv->gadget,
+                                     GTK_ORIENTATION_HORIZONTAL,
+                                     -1,
+                                     minimum_size, natural_size,
+                                     NULL, NULL);
 }
 
 static void
@@ -3842,7 +3845,11 @@ gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
                                           gint      *minimum_size,
                                           gint      *natural_size)
 {
-  gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+  gtk_css_gadget_get_preferred_size (GTK_SCROLLED_WINDOW (widget)->priv->gadget,
+                                     GTK_ORIENTATION_VERTICAL,
+                                     -1,
+                                     minimum_size, natural_size,
+                                     NULL, NULL);
 }
 
 static void